﻿//-----------------------------------------------------------------------
// <author>Paul Long</author>
//-----------------------------------------------------------------------

namespace NetworkAddresses
{
    using System;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using Microsoft.NetworkMonitor;
    using TopUserExceptions;

    /// <summary>
    /// Base class for any kind of address.
    /// </summary>
    internal class Address
    {
        /// <summary>
        /// Display string for address.
        /// </summary>
        private string display;

        /// <summary>
        /// Address type, ethernet, ipv4, or ipv6.
        /// </summary>
        private enum AddressTypes
        {
            /// <summary>
            /// Ethernet Type
            /// </summary>
            EthernetType,

            /// <summary>
            /// IPv4 address type
            /// </summary>
            IPv4Type,

            /// <summary>
            /// IPv6 address Type
            /// </summary>
            IPv6Type
        }
        
        /// <summary>
        /// Gets or sets Display string.
        /// </summary>
        public string Display
        {
            get
            {
                return this.display;
            }

            set
            {
                this.display = value;
            }
        }

        /// <summary>
        /// Creates a filter based on the address and type.
        /// </summary>
        /// <param name="address">String representation of address.</param>
        /// <returns>A filter based on the address passed in</returns>
        public static string CreateFilter(string address)
        {
            if (AddressType(address) == AddressTypes.IPv4Type)
            {
                return string.Format(CultureInfo.CurrentCulture, "(IPv4.Address == {0})", address);
            }
            else if (AddressType(address) == AddressTypes.IPv6Type)
            {
                return string.Format(CultureInfo.CurrentCulture, "(IPv6.Address == {0})", address);
            }
            else
            {
                return string.Format(CultureInfo.CurrentCulture, "(Ethernet.Address == {0})", address);
            }
        }

        /// <summary>
        /// Generic compare for greater than.
        /// </summary>
        /// <param name="leftOperand">Left Operand</param>
        /// <param name="rightOperand">Right Operand</param>
        /// <returns>True if greater than.</returns>
        public static bool operator >(Address leftOperand, Address rightOperand)
        {
            IPv4Address addrIPv4A = leftOperand as IPv4Address;
            IPv6Address addrIPv6A = leftOperand as IPv6Address;
            MachineAddress ethernetA = leftOperand as MachineAddress;

            if ((object)addrIPv4A != null)
            {
                IPv4Address addrIPv4B = rightOperand as IPv4Address;
                if (addrIPv4B == null)
                {
                    return false;
                }

                return addrIPv4A > addrIPv4B;
            }
            else if ((object)addrIPv6A != null)
            {
                IPv6Address addrIPv6B = rightOperand as IPv6Address;
                if (addrIPv6B == null)
                {
                    return false;
                }

                return addrIPv6A > addrIPv6B;
            }
            else if ((object)ethernetA != null)
            {
                MachineAddress ethernetB = rightOperand as MachineAddress;
                if (ethernetB == null)
                {
                    return false;
                }

                return ethernetA > ethernetB;
            }

            return false;
        }

        /// <summary>
        /// Generic compare for less than.
        /// </summary>
        /// <param name="leftOperand">Left Operand</param>
        /// <param name="rightOperand">Right Operand</param>
        /// <returns>True if less than</returns>
        public static bool operator <(Address leftOperand, Address rightOperand)
        {
            IPv4Address addrIPv4A = leftOperand as IPv4Address;
            IPv6Address addrIPv6A = leftOperand as IPv6Address;
            MachineAddress ethernetA = leftOperand as MachineAddress;

            if ((object)addrIPv4A != null)
            {
                IPv4Address addrIPv4B = rightOperand as IPv4Address;
                if (addrIPv4B == null)
                {
                    return false;
                }

                return addrIPv4A < addrIPv4B;
            }
            else if ((object)addrIPv6A != null)
            {
                IPv6Address addrIPv6B = rightOperand as IPv6Address;
                if (addrIPv6B == null)
                {
                    return false;
                }

                return addrIPv6A < addrIPv6B;
            }
            else if ((object)ethernetA != null)
            {
                MachineAddress ethernetB = rightOperand as MachineAddress;
                if (ethernetB == null)
                {
                    return false;
                }

                return ethernetA < ethernetB;
            }

            return false;
        }

        /// <summary>
        /// Generic compare for equality.
        /// </summary>
        /// <param name="leftOperand">Left Operand</param>
        /// <param name="rightOperand">Right Operand</param>
        /// <returns>True if equall</returns>
        public static bool operator ==(Address leftOperand, Address rightOperand)
        {
            IPv4Address addrIPv4A = leftOperand as IPv4Address;
            IPv6Address addrIPv6A = leftOperand as IPv6Address;
            MachineAddress ethernetA = leftOperand as MachineAddress;

            if ((object)addrIPv4A != null)
            {
                IPv4Address addrIPv4B = rightOperand as IPv4Address;
                if ((object)addrIPv4B == null)
                {
                    return false;
                }

                return addrIPv4A == addrIPv4B;
            }
            else if ((object)addrIPv6A != null)
            {
                IPv6Address addrIPv6B = rightOperand as IPv6Address;
                if ((object)addrIPv6B == null)
                {
                    return false;
                }

                return addrIPv6A == addrIPv6B;
            }
            else if ((object)ethernetA != null)
            {
                MachineAddress ethernetB = rightOperand as MachineAddress;
                if ((object)ethernetB == null)
                {
                    return false;
                }

                return ethernetA == ethernetB;
            }

            return false;
        }

        /// <summary>
        /// Generic compare for not equal.
        /// </summary>
        /// <param name="leftOperand">Left Operand</param>
        /// <param name="rightOperand">Right Operand</param>
        /// <returns>True if not equal</returns>
        public static bool operator !=(Address leftOperand, Address rightOperand)
        {
            IPv4Address addrIPv4A = leftOperand as IPv4Address;
            IPv6Address addrIPv6A = leftOperand as IPv6Address;
            MachineAddress ethernetA = leftOperand as MachineAddress;

            if ((object)addrIPv4A != null)
            {
                IPv4Address addrIPv4B = rightOperand as IPv4Address;
                if (addrIPv4B == null)
                {
                    return false;
                }

                return addrIPv4A != addrIPv4B;
            }
            else if ((object)addrIPv6A != null)
            {
                IPv6Address addrIPv6B = rightOperand as IPv6Address;
                if (addrIPv6B == null)
                {
                    return false;
                }

                return addrIPv6A != addrIPv6B;
            }
            else if ((object)ethernetA != null)
            {
                MachineAddress ethernetB = rightOperand as MachineAddress;
                if (ethernetB == null)
                {
                    return false;
                }

                return ethernetA != ethernetB;
            }

            return false;
        }

        /// <summary>
        /// Generic Equality
        /// </summary>
        /// <param name="obj">Object to compare</param>
        /// <returns>True if equal</returns>
        public override bool Equals(object obj)
        {
            return (Address)obj == this;
        }

        /// <summary>
        /// Returns hash code for object.
        /// </summary>
        /// <returns>Int hash code.</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determine address type length.
        /// </summary>
        /// <param name="address">String representation of the address</param>
        /// <returns>Address type</returns>
        private static AddressTypes AddressType(string address)
        {
            if (address.Length == 17)
            {
                return AddressTypes.EthernetType;
            }
            else if (address.Length > 17)
            {
                return AddressTypes.IPv6Type;
            }
            else
            {
                return AddressTypes.IPv4Type;
            }
        }
    }
}
